package entity.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;

import entity.base.BaseEntity;

@Stateless(name = "EntityController", mappedName = "EntityController")
public class BaseEntityControllerImpl implements BaseEntityController {
	
	// TODO : perisistence unit name is final here, can it be parametrezed
	@PersistenceContext(name = "bookHeaven")
	private EntityManager		entityManager;
	
	private static final int	NO_PAGE	= -1;
	
	protected void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public void delete(BaseEntity entity) {
		try {
			entityManager.remove(entity);
		}
		catch (Exception e) {
			e.printStackTrace();
			throw ExceptionConverter.convert(e);
		}
	}
	
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public BaseEntity save(BaseEntity entity) {
		try {
			if (entity.getId() != null && entityManager.find(entity.getClass(), entity.getId()) != null) {
				entityManager.merge(entity);
			}
			else {
				entityManager.persist(entity);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			throw ExceptionConverter.convert(e);
		}
		return entityManager.find(entity.getClass(), entity.getId());
	}
	
	@Override
	@TransactionAttribute(TransactionAttributeType.REQUIRED)
	public List<BaseEntity> saveBatch(List<BaseEntity> entityList) {
		for (int i = 0; i < entityList.size(); i++) {
			entityList.set(i, save(entityList.get(i)));
		}
		return entityList;
	}
	
	@Override
	public List<? extends BaseEntity> getList(Class<? extends BaseEntity> clazz) {
		return getPaginatedList(clazz, NO_PAGE, NO_PAGE);
	}
	
	@Override
	public List<? extends BaseEntity> getOrderedList(Class<? extends BaseEntity> clazz, String orderField) {
		return getPaginatedOrderedList(clazz, NO_PAGE, NO_PAGE, orderField);
	}
	
	// TODO: string based field name not type safe
	@Override
	public List<? extends BaseEntity> getPaginatedList(Class<? extends BaseEntity> clazz, int start, int end) {
		return getPaginatedOrderedList(clazz, start, end, "lastSaveDate");
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public List<? extends BaseEntity> getPaginatedOrderedList(Class<? extends BaseEntity> clazz, int start, int end,
			String orderField) {
		
		CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		CriteriaQuery<? extends BaseEntity> criteriaQuery = criteriaBuilder.createQuery(clazz);
		criteriaQuery.orderBy(criteriaBuilder.desc(criteriaQuery.from(clazz).get(orderField)));
		TypedQuery<? extends BaseEntity> typedQuery = entityManager.createQuery(criteriaQuery);
		
		if (start != NO_PAGE) {
			typedQuery.setFirstResult(start);
		}
		if (end != NO_PAGE) {
			typedQuery.setMaxResults(end);
		}
		List<? extends BaseEntity> list = typedQuery.getResultList();
		if (list == null)
			return new ArrayList(0);
		return list;
	}
	
	@Override
	public BaseEntity getById(Class<? extends BaseEntity> clazz, String id) {
		return entityManager.find(clazz, id);
	}
	
	@Override
	public BaseEntity getByField(Class<? extends BaseEntity> clazz, String field, String id) {
		CriteriaQuery<? extends BaseEntity> criteriaQuery = entityManager.getCriteriaBuilder().createQuery(clazz);
		criteriaQuery.from(clazz);
		TypedQuery<? extends BaseEntity> typedQuery = entityManager.createQuery(criteriaQuery).setParameter(field, id);
		try {
			return typedQuery.getSingleResult();
		}
		catch (NonUniqueResultException nure) {
			return typedQuery.getResultList().get(0);
		}
		catch (NoResultException nre) {
			return null;
		}
	}
	
	@Override
	public EntityManager getEntityManager() {
		return entityManager;
	}
	
	@Override
	public List<? extends BaseEntity> paginatedSearch(Class<? extends BaseEntity> clazz, String key, int start, int end) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public List<BaseEntity> search(Class<? extends BaseEntity> clazz, String key) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public List<? extends BaseEntity> getPaginatedOrderedFilteredList(Class<? extends BaseEntity> clazz, int start,
			int end, String orderField, Map<String, String> filters) {
		// TODO Auto-generated method stub
		return null;
	}
}
